Typically, software developers write applications, software systems, and services in one or more particular programming languages, using programming models geared for a fixed set of target contexts and usages. The software is then compiled, distributed and deployed as a full blown application. Whenever a particular application proves useful, its use may extend well beyond its planed life, e.g., the life of the particular language and model. Moreover, users may find uses in a variety of contexts that extend well beyond what the software developer(s) intended and anticipated. So, successful software may well outlive its original programming language(s), models and paradigm(s); some or all of the original software stack; and even, the original operating system(s), middleware and hardware on which it was based and for which was developed. Consequently, evolving—or even maintaining—such software has become increasingly expensive and problematic.
There have been a small number of techniques available to developers for making software more adaptable in somewhat limited ways. Developers may offer software in what are known as Software Product Lines (SPLs) that are designed with variation points in particular parts of the software to anticipate expected needs. As a simple example, a SPL may have an open point for adding new printers based on the assumption that all printers support Portable Document Format (PDF). The developers would be unable to extend that particular SPL to print to a Postscript printer.
Alternately, developers have used what is known as dynamic loading and dynamic weaving to provide applications capable of what is known as dynamic adaptation. Dynamic adaptation is a different technique for enabling software to work in contexts other than what it is originally developed for. More particularly, dynamic adaptation involves loading one piece of software (replacement) that replaces a functionally equivalent or similar part of an existing piece of software.
Dynamic loading involves removing a part of a running software system (a library, a component, a class, or other unit of functionality) from memory, loading a replacement, and redirecting subsequent usage from the replaced software to the replacement. Dynamic weaving involves modifying parts of running software to add or remove functionality, without need for recompilation or restarting the running software. Both dynamic loading and dynamic weaving are limited, invasive and involve potentially hazardous actions. Both techniques, for example, very easily introduce errors into running software. Failing to provide fully compatible replacement components, can cause software to produce erroneous results, go into infinite loops (cause a computer to slow down or crash), hang, or otherwise crash entirely.
Equally problematic is the cost of providing “life support” and experienced support personnel and resources, which increases with the age of the application being supported. As time passes, the size of the pool of people with skills required to maintain older software and its requisite infrastructure declines. Experienced developers are expensive, charging more for the experience. Neophyte (cheaper) developers normally train for the state of the art, ignoring outdated languages and technologies that may be necessary for maintaining the software in its required environment, i.e., providing life support.
Previously, the only alternative to continuing expensive life support was rewriting software using newer technologies. This has required extensive knowledge of new technologies, of existing software and older technologies on which the software depends, as well as knowledge of how the particular application is deployed and post-deployment operating conditions. Also, most real state of the art software is very large and complex and does extremely significant, complex things. New development is time consuming, and requires major resource expenditures, i.e., people, money, software and hardware. Consequently, previously it took at least as many people and the same level of resources to rewrite new versions of software and to ensure that new versions functioned correctly. Moreover, typical redevelopment is very likely to introduce new bugs that, potentially, cause tremendous impact on both the software users and software owners. Unfortunately, both maintaining life support and rewriting software are extremely expensive and risky alternatives.
How successful software is determines the eventual range of contexts of its use. Successful software, especially software that solves an important, ubiquitous problem, may be used and reused in a wide range of unforeseeable contexts. Consequently, planning for its reuse is very likely inadequate. Planning for variations of particular software adds to design, implementation, maintenance, evolution, test and qualification time. This all increases development and maintenance costs. When developers can predict variants with a high degree of accuracy, this cost increase may be acceptable and preferable. However, when the predictions are wrong—as they very often are—the normal result is more complex software that is unnecessarily more difficult to maintain, evolve, and adapt than it would have been otherwise, i.e., if unused variation points had not been included.
Moreover, deploying a rewrite or new version of successful software, further extends the life of the software and is likely to continue the need for further rewrites. So after deploying rewritten software, if it works well and addresses a more ubiquitous need, it may be necessary to continue evolving the software as new contexts arise. This increases the likelihood that others will take and deploy newer versions into other contexts that the developers and/or redevelopers did not originally anticipate. Consequently, adapting and modifying software for each new context has been a time-consuming and error-prone task; long-term maintenance and subsequent software evolution tends to be a nightmare. This is especially true since the above previous solutions usually required invasive modification, adapting software statically for new contexts, either manually or by using technology, e.g., aspect-oriented programming.
Thus, there is a need for simplifying extending the life of existing software and for simply and seamlessly recreating and redeploying software that addresses evolving needs that extend beyond original design goals.